#include "stdafx.h"
#include "utils/uuidgenerator.hpp"
#include "designpattern/singletonholder.hpp"
using namespace JHCPP::designpattern;


namespace JHCPP
{
	namespace utils
	{
		CUUIDGenerator::CUUIDGenerator()
		: m_ticks(0),
		m_haveNode(false)
		{
		}

		CUUIDGenerator::~CUUIDGenerator()
		{
		}

		CUUID CUUIDGenerator::create()
		{
			CMutex::ScopedLock lock(m_mutex);

			if (!m_haveNode)
			{
				CEnvironment::nodeId(m_node);
				m_haveNode = true;
			}
			CTimeStamp::UtcTimeVal tv = timeStamp();
			UInt32 timeLow = UInt32(tv & 0xFFFFFFFF);
			UInt16 timeMid = UInt16((tv >> 32) & 0xFFFF);
			UInt16 timeHiAndVersion = UInt16((tv >> 48) & 0x0FFF) + (CUUID::UUID_TIME_BASED << 12);
			UInt16 clockSeq = (UInt16(m_random.next() >> 4) & 0x3FFF) | 0x8000;
			return CUUID(timeLow, timeMid, timeHiAndVersion, clockSeq, m_node);
		}

		CUUID CUUIDGenerator::createFromName(const CUUID& nsid, const std::string& name)
		{
			CMD5Engine md5;
			return createFromName(nsid, name, md5);
		}

		CUUID CUUIDGenerator::createFromName(const CUUID& nsid, const std::string& name, CDigestEngine& de)
		{
			jh_assert_dbg (de.digestLength() >= 16);

			CUUID netNsid = nsid;
			netNsid.toNetwork();
			de.reset();
			de.update(&netNsid.m_timeLow, sizeof(netNsid.m_timeLow));
			de.update(&netNsid.m_timeMid, sizeof(netNsid.m_timeMid));
			de.update(&netNsid.m_timeHiAndVersion, sizeof(netNsid.m_timeHiAndVersion));
			de.update(&netNsid.m_clockSeq, sizeof(netNsid.m_clockSeq));
			de.update(&netNsid.m_node[0], sizeof(netNsid.m_node));
			de.update(name);
			char buffer[16];
			const CDigestEngine::Digest& d = de.digest();
			for (int i = 0; i < 16; ++i)
			{
				buffer[i] = d[i];
			}
			return CUUID(buffer, CUUID::UUID_NAME_BASED);
		}

		CUUID CUUIDGenerator::createRandom()
		{
			char buffer[16];
			//CRandomInputStream ris;
			//ris.read(buffer, sizeof(buffer));
			return CUUID(buffer, CUUID::UUID_RANDOM);
		}

		CTimeStamp::UtcTimeVal CUUIDGenerator::timeStamp()
		{
			CTimeStamp now;
			for (;;)
			{
				if (now != m_lastTime)
				{
					m_lastTime = now;
					m_ticks = 0;
					break;
				}
				if (m_ticks < 100)
				{
					++m_ticks;
					break;
				}
				now.update();
			}
			CTimeStamp::UtcTimeVal tv = now.utcTime();
			return tv + m_ticks;
		}

		CUUID CUUIDGenerator::createOne()
		{
			try
			{
				return create();
			}
			catch (CException&)
			{
				return createRandom();
			}
		}

		static TSingletonHolder<CUUIDGenerator> sh;

		CUUIDGenerator& CUUIDGenerator::defaultGenerator()
		{
			return *sh.get();
		}
	}//end of namespace utils
}//end of namespace JHCPP
